package com.tianqu.tms.core.util;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 
 * @author Huang Wei
 */
public final class Concurrents {

	/**
	 * 
	 */
	public static long initialDelay(long unit) {
		return initialDelay(unit, 0);
	}
	
	public static long initialDelay(long unit, int delta) {
		final long now = System.currentTimeMillis();
		return (now / unit + 1) * unit - now + delta * unit;
	}
	
	public static long substractTimeout(long l1, long l2) {
		final long r = (l1 < 0 ? 0 : l1) - (l2 < 0 ? 0 : l2);
		return r < 0 ? 0 : r;
	}
	
	/**
	 * 
	 */
	public static long timedWait(Object obj, long timeout)
	throws InterruptedException {
		return timedWait(obj, timeout, TimeUnit.MILLISECONDS);
	}
	
	public static long timedWait(Object obj, long timeout, TimeUnit unit)
	throws InterruptedException {
		// Precondition checking
		if(obj == null) {
			throw new IllegalArgumentException("invalid parameter obj");
		}
		if(unit == null) {
			throw new IllegalArgumentException("invalid parameter unit");
		}
		if(timeout <= 0) {
			return 0;
		}
		
		//
		final long now = System.nanoTime();
		synchronized(obj) {
			unit.timedWait(obj, timeout);
		}
		final long elapsedTime = System.nanoTime() - now;
		return substractTimeout(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
	}
	
	/**
	 * 
	 */
	public static long timedJoin(Thread thread, long timeout, TimeUnit unit)
	throws InterruptedException {
		// Precondition checking
		if(thread == null) {
			return timeout;
		}
		if(timeout <= 0) {
			return 0;
		}
		
		//
		final long now = System.nanoTime();
		unit.timedJoin(thread, timeout);
		final long elapsedTime = System.nanoTime() - now;
		return substractTimeout(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
	}
	
	public static long timedJoinQuietly(Thread thread, long timeout, TimeUnit unit) {
		final long now = System.nanoTime();
		try {
			return timedJoin(thread, timeout, unit);
		} catch(InterruptedException e) {
			Thread.currentThread().interrupt();
			final long elapsedTime = System.nanoTime() - now;
			return substractTimeout(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
		}
	}
	
	/**
	 * 
	 */
	public static void delayQuietly(long timeout) {
		try {
			delay(timeout);
		} catch(InterruptedException e) {
			Thread.currentThread().interrupt();
		}
	}
	
	public static void delayQuietly(Date date) {
		try {
			delay(date);
		} catch(InterruptedException e) {
			Thread.currentThread().interrupt();
		}
	}
	
	public static void delayQuietly(long timeout, TimeUnit unit) {
		try {
			delay(timeout, unit);
		} catch(InterruptedException e) {
			Thread.currentThread().interrupt();
		}
	}
	
	public static long delay(long timeout) throws InterruptedException {
		return delay(timeout, TimeUnit.MILLISECONDS);
	}
	
	public static long delay(Date date) throws InterruptedException {
		// Precondition checking
		if(date == null) {
			throw new IllegalArgumentException("invalid parameter date");
		}
		
		//
		return delay(date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
	}

	public static long delay(long timeout, TimeUnit unit) throws InterruptedException {
		// Precondition checking
		if(unit == null) {
			throw new IllegalArgumentException("invalid parameter unit");
		}
		if(timeout <= 0) {
			return 0;
		}
		
		//
		final Object object = new Object();
		final long now = System.nanoTime();
		synchronized(object) {
			unit.timedWait(object, timeout);
		}
		final long elapsedTime = System.nanoTime() - now;
		return substractTimeout(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
	}
	
	/**
	 * 
	 */
	public static long shutdownAndAwaitTermination(ExecutorService exec, long timeout, TimeUnit unit)
	throws InterruptedException {
		// Precondition checking
		if(exec == null) {
			return timeout;
		}
		if(!exec.isShutdown()) {
			exec.shutdown();
		}
		
		//
		if(timeout <= 0) {
			return 0;
		} else {
			final long now = System.nanoTime();
			exec.awaitTermination(timeout, unit);
			final long elapsedTime = System.nanoTime() - now;
			return substractTimeout(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
		}
	}
	
	public static long shutdownAndAwaitTerminationQuietly(ExecutorService exec, long timeout, TimeUnit unit) {
		final long now = System.nanoTime();
		try {
			return shutdownAndAwaitTermination(exec, timeout, unit);
		} catch(InterruptedException e) {
			Thread.currentThread().interrupt();
			final long elapsedTime = System.nanoTime() - now;
			return substractTimeout(timeout, unit.convert(elapsedTime, TimeUnit.NANOSECONDS));
		}
	}
}
